package isssr.dao.src;

import isssr.dao.exception.DAOException;
import isssr.dao.exception.HibernateDAOException;
import isssr.entity.src.ComparisonOperator;
import isssr.entity.src.Role;
import isssr.entity.src.User;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.SimpleExpression;
import org.hibernate.internal.CriteriaImpl.CriterionEntry;

public class HibernateUserDAO extends HibernateDAOCommons implements UserDAO, Serializable {

	public HibernateUserDAO(SessionFactory sessionFactory) {
		super(sessionFactory);
	}

	@Override
	public List<User> getAll() throws DAOException {
		
		return getAll(null, null, null);
	}

	@Override
	public List<User> getAll(Map<String, Object> filters, Integer firstResult,
			Integer pageSize) throws DAOException {
		
		Session session = null;
		ArrayList<User> userResultList = null;
		
		try
		{
			session = openSession();
			
			Criteria criteria = session.createCriteria(User.class);
			
			addPagingCriteria(criteria, firstResult, pageSize);
			addFilteringCriteria(criteria, filters);
			
			userResultList = (ArrayList<User>) criteria.list();
			
			session.flush();
		}
		catch(Exception ex)
		{
			String operationType = "GetAll";
			
			if(filters == null) 	operationType.concat("(0 filter)");
			else					operationType.concat("(" + filters.size() + " filters)");
			
			throw generateDAOException(ex, operationType);
		}
		
		return userResultList;
		
	}

	private void addFilteringCriteria(Criteria criteria,
			Map<String, Object> filters) {
		
		if(filters == null || filters.isEmpty())
			return;
		
		Object eValue;
		
		for(Entry<String, Object> entry : filters.entrySet())
		{
			eValue;
		}
	}

	private void addPagingCriteria(Criteria criteria, Integer firstResult,
			Integer pageSize) {

		if(firstResult != null && firstResult>0)
			criteria.setFirstResult(firstResult);
		
		if(pageSize != null && pageSize>0)
			criteria.setFetchSize(pageSize);
		
	}


	@Override
	public void insert(User user) throws DAOException {
		
		super.insert(user);
	}

	@Override
	public void update(User user) throws DAOException {
		
		super.update(user);
		
	}

	@Override
	public void delete(User user) throws DAOException {
		
		super.delete(user);
		
	}

	@Override
	public List<User> getEligibleSuper(User referenceUser) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<User> getEligibleSuper(User referenceUser,
			Map<String, Object> matchingFilter, Integer firstResult,
			Integer pageSize) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<User> getEligibleEnactors(User referenceUser) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<User> getEligibleEnactors(User referenceUser,
			Map<String, Object> matchingFilter, Integer firstResult,
			Integer pageSize) {
		// TODO Auto-generated method stub
		return null;
	}
	
	@Override
	public List<User> getAllByLevel(User referenceUser,
			ComparisonOperator operator) throws DAOException {

		
		return null;
	}

	@Override
	public List<User> getAllByLevel(User referenceUser,
			ComparisonOperator operator, Map<String, Object> filters,
			Integer firstResult, Integer pageSize) throws DAOException {
		

		Session session = null;
		ArrayList<User> userResultList = null;
		
		try
		{
			session = openSession();
			
			Criteria criteria = session.createCriteria(User.class);
			
			addLevelComparisonCriteria(criteria, referenceUser, operator);
			
			addPagingCriteria(criteria, firstResult, pageSize);
			addFilteringCriteria(criteria, filters);
			
			userResultList = (ArrayList<User>) criteria.list();
			
			session.flush();
		}
		catch(Exception ex)
		{
			String operationType = "GetAllByLevel";
			
			if(filters == null) 	operationType.concat("(0 filter)");
			else					operationType.concat("(" + filters.size() + " filters)");
			
			throw generateDAOException(ex, operationType);
		}
		
		return userResultList;
	}
	
	private void addLevelComparisonCriteria(Criteria criteria,
			User referenceUser, ComparisonOperator operator) throws ReflectiveOperationException {
		
		String levelPropertyName = "level";
		Number userLevel = extractLevelFromUser(referenceUser);
		
		
		Criterion expression = null;
		
		switch(operator)
		{
		case EQUAL :
			expression = Restrictions.eq(levelPropertyName, userLevel);
			break;
		case GREATER :
			expression = Restrictions.gt(levelPropertyName, userLevel);
			break;
		case GREATER_EQUAL :
			expression = Restrictions.ge(levelPropertyName, userLevel);
			break;
		case LESS :
			expression = Restrictions.lt(levelPropertyName, userLevel);
			break;
		case LESS_EQUAL :
			expression = Restrictions.le(levelPropertyName, userLevel);
			break;
		case NOT_EQUAL:
			expression = Restrictions.ne(levelPropertyName, userLevel);
			break;
		
		default :
			expression = Restrictions.eq(levelPropertyName, userLevel);
			break;
		}
		
		criteria.add(expression);
		
	}

	protected Number extractLevelFromUser(User referenceUser) throws ReflectiveOperationException 
	{
		Number levelValue = null;
		
		try
		{
			Role userRole = (Role) extractFieldFromUser(referenceUser, "role");
			
			Field field = userRole.getClass().getField("level");
			
			field.setAccessible(true);
			levelValue = (Number) field.get(userRole);
			field.setAccessible(false);
			
			return levelValue;
		}
		catch (ReflectiveOperationException ex) 
		{
			throw ex;
		}
		
		
	}
	
	protected Object extractFieldFromUser(User user, String fieldName) throws ReflectiveOperationException
	{
		try
		{
			Object fieldValue;
			
			Field field = user.getClass().getField(fieldName);
			
			field.setAccessible(true);
			fieldValue = field.get(user);
			field.setAccessible(false);

			return fieldValue;
		}
		catch (ReflectiveOperationException ex) 
		{
			throw ex;
		}
	}

	@Override
	public User getByAuth(String username, String password) throws DAOException {

		Session session = null;
		User account = null;
		
		try
		{
			session = openSession();
			
			Criteria criteria = session.createCriteria(User.class);
			
			criteria.add(Restrictions.like("username", username, MatchMode.EXACT));
			criteria.add(Restrictions.like("password", password, MatchMode.EXACT));
			
			account = (User) criteria.uniqueResult();
			
			session.flush();
		}
		catch(Exception ex)
		{
			String operationType = "GetByAuth";
			throw generateDAOException(ex, operationType);
		}
		finally
		{
			closeSession(session);
		}
		
		return account;
	}



}
